/*
 * Copyright (c) 2012 Robert Burrell Donkin robertburrelldonkin.name
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 
 * and associated documentation files (the "Software"), to deal in the Software without restriction, 
 * including without limitation the rights to use, copy, modify, merge, publish, distribute, 
 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software 
 * is furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all copies or 
 * substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING 
 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 * 
 */
package walrus.text;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.util.List;

import org.apache.commons.io.IOUtils;

public class WordCounter {
	
	private final WordCount count = new MapWordCount();
	
	private boolean ignoreCase = false;
	
	private boolean ignoreNumerals = true;

	private PrintStream out = System.out;
	
	private File outputFile = null; 
	
	public boolean isIgnoreNumerals() {
		return ignoreNumerals;
	}

	public void setIgnoreNumerals(boolean ignoreNumerals) {
		this.ignoreNumerals = ignoreNumerals;
	}

	public File getOutputFile() {
		return outputFile;
	}

	public void setOutputFile(File outputFile) {
		this.outputFile = outputFile;
	}

	public boolean isIgnoreCase() {
		return ignoreCase;
	}

	public WordCounter setIgnoreCase(boolean ignoreCase) {
		this.ignoreCase = ignoreCase;
		return this;
	}
	
	public PrintStream getOut() {
		return out;
	}

	public void setOut(PrintStream out) {
		this.out = out;
	}

	public WordCounter load(List<String> words) {
		count.clear();
		add(words);
		return this;
	}

	private void add(List<String> words) {
		for (String word: words) {
			if (ignoreCase) {
				word = word.toLowerCase();	
			}
			if (ignoreNumerals && isNumeric(word)) {
				// ignore
			} else {
				count.inc(word);
			}
		}
	}
	
	private boolean isNumeric(String string) {
		return isIntegral(string) || isDecimal(string);
	}

	private boolean isIntegral(String string) {
		try {
			Long.valueOf(string);
			return true;
		} catch (NumberFormatException e) {
			return false;
		}
	}

	private boolean isDecimal(String string) {
		try {
			Double.valueOf(string);
			return true;
		} catch (NumberFormatException e) {
			return false;
		}
	}

	public WordCounter printTo(PrintStream out) {
		out.println("# words: " + count.size());
		int i = 0;
		WordCount.Walk walk = count.descending();
		while (walk.hasNext()) {
			if (++i % 100 == 0) out.println("# @" + i);
			out.println(walk.next() + " " + walk.count());
		}
		out.println("#");
		out.println("# Just Words");
		out.println("#");
		i = 0;
		walk = count.descending();
		while (walk.hasNext()) {
			if (++i % 100 == 0) out.println("# @" + i);
			out.println(walk.next());
		}
		out.flush();
		return this;
	}

	public List<String> act(List<String> words) throws Exception {
		add(words);
		return words;
	}
	
	public WordCount act(WordCount wordCount) throws Exception {
		count.merge(wordCount);
		return wordCount;
	}

	public WordCount getCount() {
		return count;
	}
	
	public void finish() {
		System.out.println("[RESULTS] Printing...");
		boolean closeStream = false;
		if (outputFile != null) {
			try {
				out = new PrintStream(new BufferedOutputStream(new FileOutputStream(outputFile)));
				closeStream = true;
			} catch (FileNotFoundException e) {
				e.printStackTrace();
				out = null;
			}
		}
		if (out == null) {
			out = System.out;
		}
		printTo(out);
		System.out.println("[RESULTS] Done.");
		if (closeStream) {
			IOUtils.closeQuietly(out);
		}
	}
}
